package com.hilinos.gb.gb28181;

import com.hilinos.gb.gb28181.config.SipConfig;
import com.hilinos.gb.gb28181.processor.SipProcessorFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import javax.sip.*;
import javax.sip.message.Response;
import java.util.Properties;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class SipServer implements SipListener {

    /**
     * 处理器数量
     */
    private static final int PROC_NUMBER = Runtime.getRuntime().availableProcessors();

    /**
     * 最大队列数量
     */
    private static final int MAX_QUEUE = 100000;

    /**
     * 消息处理器线程池
     */
    private static final ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(PROC_NUMBER, PROC_NUMBER * 5,
            0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(MAX_QUEUE),
            new ThreadPoolExecutor.CallerRunsPolicy());

    @Autowired
    private ApplicationContext context;

    @Bean("sipFactory")
    private SipFactory sipFactory() {
        SipFactory sipFactory = SipFactory.getInstance();
        sipFactory.setPathName("gov.nist");
        return sipFactory;
    }

    @Bean("sipStack")
    @DependsOn({ "sipFactory", "sipConfig" })
    public SipStack startTcpProvider(SipFactory sipFactory, SipConfig sipConfig) throws PeerUnavailableException {
        Properties properties = new Properties();
        properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP");
        properties.setProperty("javax.sip.IP_ADDRESS", sipConfig.getSipIp());
        return sipFactory.createSipStack(properties);
    }

    @Bean("udpProvider")
    @DependsOn("sipStack")
    public SipProvider udpProvider(SipStack sipStack, SipConfig sipConfig) throws Exception {
        ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(),
                "UDP");
        SipProvider udpProvider = sipStack.createSipProvider(udpListeningPoint);
        udpProvider.addSipListener(this);
        return udpProvider;
    }

    @Bean("tcpProvider")
    @DependsOn("sipStack")
    public SipProvider tcpProvider(SipStack sipStack, SipConfig sipConfig) throws Exception {
        ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(),
                "TCP");
        SipProvider tcpProvider = sipStack.createSipProvider(udpListeningPoint);
        tcpProvider.addSipListener(this);
        return tcpProvider;
    }

    /**
     * @param transport TCP或者UDP
     */
    public SipProvider getProvider(String transport) throws Exception {
        switch (transport) {
            case "TCP":
                return context.getBean("tcpProvider", SipProvider.class);
            case "UDP":
                return context.getBean("udpProvider", SipProvider.class);
            default:
                throw new Exception("transport无效");
        }
    }

    @Override
    public void processRequest(RequestEvent requestEvent) {
        POOL_EXECUTOR.execute(() -> {
            try {
                SipProcessorFactory factory = context.getBean(SipProcessorFactory.class);
                factory.createRequestProcessorFactory(requestEvent).process(requestEvent);
                factory.responseAck(requestEvent);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    @Override
    public void processResponse(ResponseEvent responseEvent) {
        Response response = responseEvent.getResponse();
        int status = response.getStatusCode();
        // 只处理成功的响应
        if ((status >= 200) && (status < 300)) {
            try {
                context.getBean(SipProcessorFactory.class).createResponseProcessorFactory(responseEvent)
                        .process(responseEvent);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if ((status >= 100) && (status < 200)) {
            // 增加其它无需回复的响应，如101、180等
        } else {
            log.warn("接收到失败的response响应！\n" +
                    "reason: {}\n" +
                    "body: {}", responseEvent.getResponse().getReasonPhrase(), responseEvent.getResponse().toString());
        }

    }

    @Override
    public void processTimeout(TimeoutEvent timeoutEvent) {

    }

    @Override
    public void processIOException(IOExceptionEvent ioExceptionEvent) {

    }

    @Override
    public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {

    }

    @Override
    public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {

    }
}
